/*
 * Copyright (C) 2007 IsmAvatar <cmagicj@nni.com>
 * 
 * This file is part of Conflict Map Editor (CME).
 * CME is free software and comes with ABSOLUTELY NO WARRANTY.
 * See LICENSE for details.
 */

package org.conflict.main;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JPanel;

public class EditorArea extends JPanel
	{
	private static final long serialVersionUID = 1L;

	private MapEditor res;
	private Point dragStart, flagR, flagB;
	private HashMap<Point,Integer> floors, objects, ceilings;
	private BufferedImage dragger;

	public EditorArea(MapEditor parent)
		{
		res = parent;
		dragStart = null;
		floors = new HashMap<Point,Integer>();
		objects = new HashMap<Point,Integer>();
		ceilings = new HashMap<Point,Integer>();
		MouseAdapter2 ma2 = new MouseAdapter2();
		addMouseListener(ma2);
		addMouseMotionListener(ma2);
		//		enableEvents(MouseEvent.MOUSE_PRESSED);
		}

	public void paintComponent(Graphics g2)
		{
		super.paintComponent(g2);
		int w = res.sWidth.getIntValue();
		int h = res.sHeight.getIntValue();
		int tw = MapEditor.TILE_WIDTH;
		int th = MapEditor.TILE_HEIGHT;
		int zw = scale(w * tw);
		int zh = scale(h * th);
		BufferedImage bi = new BufferedImage(w * tw,h * th,BufferedImage.TYPE_3BYTE_BGR);
		Graphics g = bi.getGraphics();
		g.setColor(Color.BLACK);
		g.fillRect(0,0,w * tw,h * th);
		if (res.fShow.isSelected())
			{
			for (Map.Entry<Point,Integer> e : floors.entrySet())
				{
				Point p = e.getKey();
				Integer i = e.getValue();
				if (res.cShow.isSelected() && ceilings.get(e) != null) continue;
				if (i == null || i.equals(new Integer(0))) continue;
				g.drawImage(res.floor.getImage(i),p.x * tw,p.y * th,null);
				}
			}
		if (res.oShow.isSelected())
			{
			for (Map.Entry<Point,Integer> e : objects.entrySet())
				{
				Point p = e.getKey();
				Integer i = e.getValue();
				if (res.cShow.isSelected() && ceilings.get(e) != null) continue;
				if (i == null || i.equals(new Integer(0))) continue;
				g.drawImage(res.object.getImage(i),p.x * tw,p.y * th,null);
				}
			}
		if (res.cShow.isSelected())
			{
			for (Map.Entry<Point,Integer> e : ceilings.entrySet())
				{
				Point p = e.getKey();
				Integer i = e.getValue();
				if (i == null || i.equals(new Integer(0))) continue;
				g.drawImage(res.ceiling.getImage(i),p.x * tw,p.y * th,null);
				}
			}
		g2.drawImage(bi,0,0,zw,zh,null);
		if (res.sGrid.isSelected())
			{
			g2.setColor(res.sColor.getSelectedColor());
			for (int x = 1; x < w; x++)
				g2.drawLine(scale(x * tw),0,scale(x * tw),zh - 1);
			for (int y = 1; y < h; y++)
				g2.drawLine(0,scale(y * th),zw - 1,scale(y * th));
			}
		if (dragStart != null)
			{
			if (dragger == null) dragger = MapEditor.getImage("actions/run.png");
			g2.drawImage(dragger,dragStart.x - dragger.getWidth() / 2,dragStart.y - dragger.getHeight()
					/ 2,null);
			}
		}

	/** Convenience method to apply the zoom factor to a given size/coordinate piece */
	public int scale(int x)
		{
		if (res.sZoom == 100) return x;
		return (int) ((double) x * (((double) res.sZoom) / 100.0));
		}

	public Dimension getPreferredSize()
		{
		int w = res.sWidth.getIntValue();
		int h = res.sHeight.getIntValue();
		int tw = MapEditor.TILE_WIDTH;
		int th = MapEditor.TILE_HEIGHT;
		int zw = scale(w * tw);
		int zh = scale(h * th);
		return new Dimension(zw,zh);
		}

	/** Clears out all floors, objects, and ceilings, thus starting this EditorArea anew */
	public void flush()
		{
		dragStart = null;
		floors.clear();
		objects.clear();
		ceilings.clear();
		updateUI();
		}

	/** Returns whether all the necessary objects (flags, spawn points) exist in this room */
	public boolean hasNecessaryObjects()
		{
		return objects.containsValue(1) && objects.containsValue(2) && objects.containsValue(7)
				&& objects.containsValue(8);
		}

	/** Provided for FileHandler.load(File). Puts all <i>values</i> into the map at row <i>y</i>. */
	public void putRow(int tab, int y, String[] values)
		{
		HashMap<Point,Integer> map = null;
		switch (tab)
			{
			case 0:
				map = floors;
				break;
			case 1:
				map = objects;
				break;
			case 2:
				map = ceilings;
				break;
			}
		for (int x = 0; x < values.length; x++)
			map.put(new Point(x,y),res.fh.lookup(values[x]));
		}

	//This should be made more efficient
	/** Adds the tile to the map, taking into consideration Delete Underlying, etc */
	private void put(HashMap<Point,Integer> map, int button, int x, int y, int value, int other)
		{
		Point p = new Point(x,y);
		Integer i2 = map.get(p);
		int i = (i2 == null) ? 0 : i2.intValue();
		if (res.sDeleteUnder.isSelected() || i == 0 || value == 0)
			{
			map.put(p,value);
			return;
			}
		//there is a tile in the way, and it may be locked
		//1) if we're not dealing with floors, it is locked
		//2) if the tile is the same as this tile, there's no sense overwriting it
		if (map != floors || i == value) return;
		//there is a floor tile in the way, and it may be locked
		//3) if it is not a space tile or a gray tile, it is locked 
		if (i != 1 && i != 3) return;
		//4) space tiles are locked
		if (i == 1 && !res.fDelSpace.isSelected()) return;
		//5) gray tiles are locked
		if (i == 3 && !res.fDelGray.isSelected()) return;
		//6) it is an unlocked space or gray tile
		map.put(p,value);
		}

	/** Returns the tile found at this position, or 0 if there is none */
	public int getMapped(int tab, int x, int y)
		{
		HashMap<Point,Integer> map = null;
		switch (tab)
			{
			case 0:
				map = floors;
				break;
			case 1:
				map = objects;
				break;
			case 2:
				map = ceilings;
				break;
			}
		Integer i = map.get(new Point(x,y));
		if (i == null) return 0;
		return i.intValue();
		}

	private class MouseAdapter2 extends MouseAdapter
		{
		public void placeTile(int button, int x, int y, boolean fill)
			{
			HashMap<Point,Integer> map = null;
			int left = 0, right = 0;
			switch (res.tabs.getSelectedIndex())
				{
				case 1:
					map = objects;
					left = res.object.getLeft();
					right = res.object.getRight();
					break;
				case 2:
					map = ceilings;
					left = res.ceiling.getLeft();
					right = res.ceiling.getRight();
					break;
				default:
					map = floors;
					left = res.floor.getLeft();
					right = res.floor.getRight();
					break;
				}
			int value = (button == MouseEvent.BUTTON1) ? left : right;
			int other = (button == MouseEvent.BUTTON3) ? left : right;

			int x1 = (int) (((double) x) / scale(MapEditor.TILE_WIDTH));
			int y1 = (int) (((double) y) / scale(MapEditor.TILE_HEIGHT));

			if (map == objects)
				{
				if (value == 1)
					{
					if (flagB != null) map.remove(flagB);
					flagB = new Point(x1,y1);
					if (fill)
						{
						dragStart = null;
						fill = false;
						}
					}
				if (value == 2)
					{
					if (flagR != null) map.remove(flagR);
					flagR = new Point(x1,y1);
					if (fill)
						{
						dragStart = null;
						fill = false;
						}
					}
				}

			if (!fill)
				{
				put(map,button,x1,y1,value,other);
				updateUI();
				return;
				}

			int x2 = (int) (((double) dragStart.x) / scale(MapEditor.TILE_WIDTH));
			int y2 = (int) (((double) dragStart.y) / scale(MapEditor.TILE_HEIGHT));
			if (x2 < x1)
				{
				int xx = x1;
				x1 = x2;
				x2 = xx;
				}
			if (y2 < y1)
				{
				int yy = y1;
				y1 = y2;
				y2 = yy;
				}
			for (int yy = y1; yy <= y2; yy++)
				for (int xx = x1; xx <= x2; xx++)
					put(map,button,xx,yy,value,other);
			dragStart = null;
			updateUI();
			}

		public void mousePressed(MouseEvent e)
			{
			if (e.getX() >= getPreferredSize().width && e.getY() >= getPreferredSize().height) return;
			if (e.getButton() != MouseEvent.BUTTON1 && e.getButton() != MouseEvent.BUTTON3) return;
			if (res.sRect.isSelected())
				{
				if (dragStart != null)
					dragStart = null;
				else
					dragStart = e.getPoint();
				updateUI();
				return;
				}
			placeTile(e.getButton(),e.getX(),e.getY(),false);
			}

		public void mouseDragged(MouseEvent e)
			{
			if (e.getX() >= getPreferredSize().width && e.getY() >= getPreferredSize().height) return;
			int b = e.getModifiers() & (MouseEvent.BUTTON1_MASK | MouseEvent.BUTTON3_MASK);
			if (b == 0) return;
			if (res.sRect.isSelected() || dragStart != null) return;
			placeTile(b == MouseEvent.BUTTON1_MASK ? MouseEvent.BUTTON1 : MouseEvent.BUTTON3,e.getX(),
					e.getY(),false);
			}

		public void mouseReleased(MouseEvent e)
			{
			if (e.getX() >= getPreferredSize().width && e.getY() >= getPreferredSize().height) return;
			if (e.getButton() != MouseEvent.BUTTON1 && e.getButton() != MouseEvent.BUTTON3) return;
			if (dragStart == null) return;
			placeTile(e.getButton(),e.getX(),e.getY(),true);
			}
		}
	}
